﻿// $Id: ActivityState.cs 10 2008-02-29 15:58:02Z mcartoixa $
/*
 *  Copyright (C) 2007 Noury Solutions
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 3 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *
 */
/*! \file
 * \brief Definition of \c Salamanca.DataActivities.ActivityState.
 *
 * \author $Author: mcartoixa $
 * \date $Date: 2008-02-29 16:58:02 +0100 (ven., 29 févr. 2008) $
 * \version $Revision: 10 $
 */

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Text;
using System.Threading;
using Salamanca.DataRules;

namespace Salamanca.DataActivities
{



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Delegate used to create a new activity state.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public delegate ActivityState<TActivityData> NextStateCreateHandler<TActivityData>(TActivityData data, IActivityParameters parameters)
        where TActivityData:
            IActivityData;



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Result of a state processing.</summary>
    /// <seealso cref="IActivityState.Handle(IActivityController)" />
    ///
    ///////////////////////////////////////////////////////////////////////////

    public enum ActivityStateResult
    {
        /// <summary>The process is canceled.</summary>
        Cancel,
        /// <summary>The process is completed.</summary>
        End,
        /// <summary>The process is halted.</summary>
        Stop,
        /// <summary>The process steps through its next state.</summary>
        Next,
        /// <summary>The process steps back to its previous state.</summary>
        Previous
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Defines the arguments for an <see cref="IActivityState">activity state</see> event.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public sealed class ActivityStateEventArgs:
        EventArgs
    {

        /// <summary>Initializes a new instance of the <see cref="ActivityStateEventArgs"/> class.</summary>
        /// <param name="controller">The controller.</param>
        public ActivityStateEventArgs(IActivityController controller)
        {
            _Controller=controller;
        }

        /// <summary>Gets the controller in charge of the activity execution.</summary>
        /// <value>The controller in charge of the activity execution.</value>
        public IActivityController Controller
        {
            get
            {
                return _Controller;
            }
        }

        /// <summary>Gets controller in charge of the activity execution.</summary>
        private IActivityController _Controller;
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Interface of an activity state.</summary>
    /// <remarks>
    /// <para>A business activity is a <a href="http://en.wikipedia.org/wiki/Finite_state_machine">finite state machine</a>.
    /// A business activity state is an <see cref="IActivityState" /> specialization.</para>
    /// </remarks>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public interface IActivityState:
        IValidatable,
        IValidationExtenderProvider,
        IDataErrorInfo
    {

        /// <summary>Initializes the current state in the context of the specified <paramref name="controller" />.</summary>
        /// <param name="controller">The activity controller.</param>
        void Initialize(IActivityController controller);

        /// <summary>Terminates the current state in the context of the specified <paramref name="controller" />.</summary>
        /// <param name="controller">The activity controller.</param>
        void Terminate(IActivityController controller);


        /// <summary>Processes the state.</summary>
        /// <param name="controller">The activity controller.</param>
        ActivityStateResult Handle(IActivityController controller);

        /// <summary>Gets the next state in the state machine.</summary>
        IActivityState NextState
        {
            get;
        }

        /// <summary>The activity data.</summary>
        IActivityData Data
        {
            get;
        }

        /// <summary>Raised when the state is initialized.</summary>
        event EventHandler<ActivityStateEventArgs> Initialized;
        /// <summary>Raised when the state is terminated.</summary>
        event EventHandler<ActivityStateEventArgs> Terminated;
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Interface of an activity initial state.</summary>
    /// <remarks>
    /// <para>This interface is a marker. An activity can only be initiated by a state that implements this interface.</para>
    /// </remarks>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public interface IActivityInitialState:
        IActivityState
    {
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Interface of an activity parameters class.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public interface IActivityParameters
    {
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Interface of an activity parameters class that gives access to a question factory.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public interface IQuestionFactoryParameters<TQuestionFactory>:
        IActivityParameters
    {

        /// <summary>Gets the question factory.</summary>
        TQuestionFactory QuestionFactory { get; }
    }



    ///////////////////////////////////////////////////////////////////////////
    ///
    /// <summary>Base class for an activity state implementation.</summary>
    ///
    ///////////////////////////////////////////////////////////////////////////

    public abstract class ActivityState<TActivityData>:
        IActivityState,
        IActivityStateMementoOriginator
        where TActivityData:
            IActivityData
    {

        /// <summary>Initializes a new <see cref="ActivityState{TActivityData}" /> instance.</summary>
        private ActivityState()
        {
        }

        /// <summary>Initializes a new <see cref="ActivityState{TActivityData}" /> instance.</summary>
        protected ActivityState(TActivityData data)
        {
            Debug.Assert(data!=null);
            if (data==null)
                throw new ArgumentNullException("data");

            _Data=data;
        }

        /// <summary>Initializes a new <see cref="ActivityState{TActivityData}" /> instance.</summary>
        protected ActivityState(TActivityData data, IActivityParameters parameters):
            this(data)
        {
            _Parameters=parameters;
        }

        #region IValidatable
        /// <summary>Validates the current activity state.</summary>
        /// <returns><c>true</c> if valid ; <c>false</c> if not.</returns>
        public bool Validate()
        {
            return Validate(null);
        }

        private bool Validate(string propertyName)
        {
            OnValidating(new ValidationEventArgs(propertyName));

            return DataRules.Rules.Validate(this, propertyName, Rules);
        }

        private ReadOnlyCollection<IRule> GetBrokenRules(string propertyName)
        {
            OnValidating(new ValidationEventArgs(propertyName));

            return DataRules.Rules.GetBrokenRules(this, propertyName, Rules);
        }

        bool IValidatable.Validate(string propertyName)
        {
            return Validate(propertyName);
        }

        IList<IRule> IValidatable.GetBrokenRules()
        {
            return GetBrokenRules(null);
        }


        IList<IRule> IValidatable.GetBrokenRules(string propertyName)
        {
            return GetBrokenRules(propertyName);
        }

        /// <summary>Raises the <see cref="Validating" /> event.</summary>
        /// <remarks>
        ///   <include file="../doc/Global.doc.xml" path="doc/template[@name='OnEvent']/*" />
        /// </remarks>
        protected virtual void OnValidating(ValidationEventArgs e)
        {
            if (Validating!=null)
                Validating(this, e);
        }

        private IList<IRule> Rules
        {
            get
            {
                // Get the list of intrinsic rules
                if (_Rules==null)
                {
                    IList<IRule> tmp=CreateRules();

                    // Validate ... the rules !
                    foreach (IRule r in tmp)
                    {
                        // The property name must be valid for this type
                        bool check=
                            (r.PropertyName.Length==0)||
                            (GetType().GetProperty(r.PropertyName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy)!=null);
                        Debug.Assert(check);
                        if (!check)
                            throw new InvalidOperationException();
                    }

                    _Rules=tmp;
                }

                return _Rules;
            }
        }

        IList<IRule> IValidatable.Rules
        {
            get
            {
                return Rules;
            }
        }
        #endregion

        #region IValidationExtenderProvider
        /// <summary>Creates the extended business rules that applies to the specified target.</summary>
        /// <returns>The list of extended business rules created.</returns>
        protected virtual IList<IRule> GetExtendedRules(IValidationExtensible extendee)
        {
            return new List<IRule>();
        }

        IList<IRule> IValidationExtenderProvider.GetExtendedRules(IValidationExtensible extendee)
        {
            return GetExtendedRules(extendee);
        }
        #endregion

        #region IDataErrorInfo
        /// <summary>Gets an error message indicating what is wrong with this object.</summary>
        string IDataErrorInfo.Error
        {
            get
            {
                string ret=((IDataErrorInfo)this)[string.Empty];

                return ret ?? string.Empty;
            }
        }

        /// <summary>Gets the error message for the property with the given name.</summary>
        /// <param name="propertyName">The name of the property whose error message to get.</param>
        /// <returns>The error message for the property. The default is an empty string ("").</returns>
        string IDataErrorInfo.this[string propertyName]
        {
            get
            {
                return DataRules.Rules.GetMessage(GetBrokenRules(propertyName));
            }
        }
        #endregion


        /// <summary>Creates the business rules used to <see cref="Validate()"/> the process state. </summary>
        /// <returns>The list of business rules created.</returns>
        protected virtual IList<IRule> CreateRules()
        {
            return DataRules.Rules.CreateRules(GetType());
        }

        /// <summary>Gets the next state in the state machine.</summary>
        protected abstract IActivityState NextState
        {
            get;
        }

        /// <summary>Processes the state.</summary>
        /// <param name="controller">The controller for the process.</param>
        protected abstract ActivityStateResult Handle(IActivityController controller);

        /// <summary>Raises the <see cref="Initialized" /> event.</summary>
        protected virtual void OnInitialized(ActivityStateEventArgs e)
        {
            if (Initialized!=null)
                Initialized(this, e);
        }

        /// <summary>Raises the <see cref="Terminated" /> event.</summary>
        protected virtual void OnTerminated(ActivityStateEventArgs e)
        {
            if (Terminated!=null)
                Terminated(this, e);
        }

        void IActivityState.Terminate(IActivityController controller)
        {
            OnTerminated(new ActivityStateEventArgs(controller));
        }

        void IActivityState.Initialize(IActivityController controller)
        {
            OnInitialized(new ActivityStateEventArgs(controller));
        }

        /// <summary>Processes the state.</summary>
        /// <param name="controller">The controller for the process.</param>
        ActivityStateResult IActivityState.Handle(IActivityController controller)
        {
            return Handle(controller);
        }

        /// <summary>Gets the next state in the state machine.</summary>
        /// <returns>The next process state in the state machine.</returns>
        IActivityState IActivityState.NextState
        {
            get
            {
                return NextState;
            }
        }

        IActivityStateMemento IActivityStateMementoOriginator.CreateMemento()
        {
            return new ActivityStateMemento(this);
        }

        /// <summary>The activity data.</summary>
        protected TActivityData Data
        {
            get
            {
                return _Data;
            }
        }

        /// <summary>The activity parameters.</summary>
        protected IActivityParameters Parameters
        {
            get
            {
                return _Parameters;
            }
        }

        /// <summary>The activity data.</summary>
        IActivityData IActivityState.Data
        {
            get
            {
                return Data;
            }
        }

        /// <summary>Raised when the state is initialized.</summary>
        public event EventHandler<ActivityStateEventArgs> Initialized;
        /// <summary>Raised when the state is terminated.</summary>
        public event EventHandler<ActivityStateEventArgs> Terminated;
        /// <summary>Occurs when the instance is about to be validated.</summary>
        public event EventHandler<ValidationEventArgs> Validating;


        private IList<IRule> _Rules;
        /// <summary>The activity data.</summary>
        private TActivityData _Data;
        /// <summary>The activity parameters.</summary>
        private IActivityParameters _Parameters;

    }

}
